package middleware.llvm;

import middleware.llvm.enums.BinaryOpType;
import middleware.llvm.enums.CmpType;
import middleware.llvm.enums.IRType;
import middleware.llvm.instruction.*;
import middleware.llvm.value.BasicIRType;
import middleware.llvm.value.FunctionIRType;
import middleware.llvm.value.ImmediateValue;
import middleware.llvm.value.Value;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 基本块以及块内语句的创建
 *
 * @author 杨凯伟
 * @date 2024/10/30
 */
public class BasicBlock extends Value {
    private final List<Instruction> instructions = new ArrayList<>();
    private final FunctionIRType function;
    private int loopNum = 0;

    public BasicBlock(FunctionIRType belongFunc) {
        super(new BasicIRType(IRType.LABEL));
        this.function = belongFunc;
    }

    public int getLoopNum() {
        return loopNum;
    }

    public void setLoopNum(int loopNum) {
        this.loopNum = loopNum;
    }

    public FunctionIRType getFunction() {
        return function;
    }

    private Integer tryGetImmediateValue(Value value) {//获取立即数
        return value instanceof ImmediateValue ivalue ? ivalue.getValue() : null;
    }

    public BasicBlock getNextBasicBlock() {
        var blocks = function.getBasicBlocks();
        var idx = blocks.indexOf(this);
        return (idx == -1 || idx == blocks.size() - 1) ? null : blocks.get(idx + 1);
    }

    private Value insertInstruction(Instruction inst) {
        instructions.add(inst);
        inst.setBasicBlock(this);
        return inst;
    }


    public Value createAndInst(Value left, Value right) {//创建与指令
        Integer immediateLeft = tryGetImmediateValue(left), immediateRight = tryGetImmediateValue(right);
        if (immediateLeft != null && immediateRight != null) {// 如果两个操作数都是常量，直接计算结果
            return new ImmediateValue(immediateLeft & immediateRight);
        }
        if (immediateLeft != null) {
            if (immediateLeft == 0) {// 0 & a = 0
                return new ImmediateValue(0);
            }
            if (immediateLeft == -1) {// -1 & a = a
                return right;
            }
        }

        if (immediateRight != null) {
            if (immediateRight == 0) {// a & 0 = 0
                return new ImmediateValue(0);
            }
            if (immediateRight == -1) {// a & -1 = a
                return left;
            }
        }
        // 如果没有优化，插入新的指令
        return insertInstruction(new BinaryInstruction(BinaryOpType.AND, left, right));
    }

    public Value createORInst(Value left, Value right) {//创建或指令
        Integer immediateLeft = tryGetImmediateValue(left), immediateRight = tryGetImmediateValue(right);

        if (immediateLeft != null && immediateRight != null) {// 如果两个操作数都是常量，直接计算结果
            return new ImmediateValue(immediateLeft | immediateRight);
        }

        if (immediateLeft != null) {
            if (immediateLeft == 0) {// 0 | a = a
                return right;
            }
            if (immediateLeft == -1) {// -1 | a = -1
                return new ImmediateValue(-1);
            }
        }

        if (immediateRight != null) {
            if (immediateRight == 0) {// a | 0 = a
                return left;
            }
            if (immediateRight == -1) {// a | -1 = -1
                return new ImmediateValue(-1);
            }
        }
        // 如果没有优化，插入新的指令
        return insertInstruction(new BinaryInstruction(BinaryOpType.OR, left, right));
    }

    public Value createAddInst(Value left, Value right) {//创建加法指令
        Integer immediateLeft = tryGetImmediateValue(left), immediateRight = tryGetImmediateValue(right);
        if (immediateLeft != null && immediateRight != null) {
            return new ImmediateValue(immediateLeft + immediateRight);
        }
        if (immediateLeft != null && immediateLeft == 0) { // 0 + a = a
            return right;
        }
        if (immediateRight != null && immediateRight == 0) { // a + 0 = a
            return left;
        }

        return insertInstruction(new BinaryInstruction(BinaryOpType.ADD, left, right));
    }

    public Value createSubInst(Value left, Value right) {
        Integer immediateLeft = tryGetImmediateValue(left), immediateRight = tryGetImmediateValue(right);
        if (immediateLeft != null && immediateRight != null) {
            return new ImmediateValue(immediateLeft - immediateRight);
        }
        if (immediateRight != null && immediateRight == 0) { // a - 0 = a
            return left;
        }
        if (left == right) { // a - a = 0
            return new ImmediateValue(0);
        }

        return insertInstruction(new BinaryInstruction(BinaryOpType.SUB, left, right));
    }

    public Value createMulInst(Value left, Value right) {
        Integer immediateLeft = tryGetImmediateValue(left), immediateRight = tryGetImmediateValue(right);
        if (immediateLeft != null && immediateRight != null) {
            return new ImmediateValue(immediateLeft * immediateRight);
        }
        if (immediateLeft != null && immediateLeft == 0) { // 0 * a = 0
            return new ImmediateValue(0);
        }
        if (immediateRight != null && immediateRight == 0) { // a * 0 = 0
            return new ImmediateValue(0);
        }
        if (immediateLeft != null && immediateLeft == 1) { // 1 * a = a
            return right;
        }
        if (immediateRight != null && immediateRight == 1) { // a * 1 = a
            return left;
        }

        return insertInstruction(new BinaryInstruction(BinaryOpType.MUL, left, right));
    }

    public Value createSDivInst(Value left, Value right) {//符号除法
        Integer immediateLeft = tryGetImmediateValue(left), immediateRight = tryGetImmediateValue(right);
        if (immediateLeft != null && immediateRight != null) {
            return new ImmediateValue(immediateLeft / immediateRight);
        }
        if (immediateRight != null && immediateRight == 1) { // a / 1 = a
            return left;
        }
        if (immediateLeft != null && immediateLeft == 0) { // 0 / a = 0
            return new ImmediateValue(0);
        }
        if (left == right) { // a / a = 1
            return new ImmediateValue(1);
        }

        return insertInstruction(new BinaryInstruction(BinaryOpType.SDIV, left, right));
    }

    public Value createSRemInst(Value left, Value right) {//取余
        Integer immediateLeft = tryGetImmediateValue(left), immediateRight = tryGetImmediateValue(right);
        if (immediateLeft != null && immediateRight != null) {
            return new ImmediateValue(immediateLeft % immediateRight);
        }
        return insertInstruction(new BinaryInstruction(BinaryOpType.SREM, left, right));
    }

    public Value createAllocaInstAndInsertToFront(middleware.llvm.value.IRType type) {
        var allocaInst = new AllocaInstruction(type);
        int insertPos = 0;
        while (insertPos < instructions.size() && instructions.get(insertPos) instanceof AllocaInstruction)
            insertPos++;
        instructions.add(insertPos, allocaInst);
        allocaInst.setBasicBlock(this);
        return allocaInst;
    }

    public void createReturnInst(Value value) {
        var inst = value == null ? new ReturnInstruction() : new ReturnInstruction(value);
        insertInstruction(inst);
    }

    public Value createLoadInst(Value ptr) {
        return insertInstruction(new LoadInstruction(ptr));
    }

    public void createStoreInst(Value value, Value ptr) {
        insertInstruction(new StoreInstruction(value, ptr));
    }

    public Value createCallInst(FunctionIRType func, List<Value> params) {
        return insertInstruction(new CallInstruction(func, params));
    }

    public Value createAllocaInst(middleware.llvm.value.IRType type) {
        return insertInstruction(new AllocaInstruction(type));
    }


    public Value createICmpInst(CmpType cond, Value left, Value right) {
        return insertInstruction(new ICmpInstruction(cond, left, right));
    }

    public Value createBrInstWithCond(Value cond, BasicBlock ifTrue, BasicBlock ifFalse) {
        return insertInstruction(new BrInstruction(cond, ifTrue, ifFalse));
    }

    public Value createBrInstWithoutCond(BasicBlock dest) {
        return insertInstruction(new BrInstruction(dest));
    }

    public Value createGetElementPtrInst(Value elementBase, List<Value> offsets) {
        return insertInstruction(new GetElementPtrInstruction(elementBase, offsets));
    }

    public Value createZExtInst(middleware.llvm.value.IRType dstType, Value value) {
        return insertInstruction(new ZExtInstruction(dstType, value));
    }

    public List<Instruction> getInstructions() {
        return instructions;
    }

    @Override
    public String getName() {
        return "%b" + super.getName();
    }

    public void dump(PrintStream out) {
        out.printf("b%s:\n", super.getName());
//        for (var inst : instructions) {
//            inst.dump(out);
//        }
        instructions.forEach(inst -> inst.dump(out));
        out.print("\n");
    }
}
